package top.gelonggld.action;


import org.jetbrains.annotations.Nullable;
import top.gelonggld.constants.Cons;
import top.gelonggld.constants.PType;
import top.gelonggld.constants.Rex;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiManager;
import top.gelonggld.model.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ChooseDialog extends JDialog {
    private JPanel contentPane;
    private JButton buttonOK;
    private JButton buttonCancel;
    private JPanel listContent;
    private JPanel panel;
    StringBuffer newStr;
    Project project;
    String baseStr ;
    int subIndex = -1 ;
    ArrayList<PanelRow> panelRows ;
    ClassContent classContent;
    ClassHeader classHeader;
    VirtualFile virtualFile;
    public ChooseDialog(Project project , VirtualFile virtualFile) {
        virtualFile.refresh(false,false);
        setContentPane(contentPane);
        setModal(true);
        getRootPane().setDefaultButton(buttonOK);
        this.virtualFile = virtualFile;
        this.project = project ;
        panelRows = new ArrayList<>();
        try {
            baseStr = new String(virtualFile.contentsToByteArray(),"UTF-8");
            classContent = classContent(baseStr);
            classContent.paragraphs.add(0,classHeader);
            for(int i = 0 ; i < classContent.paragraphs.size(); i ++ ){
                Paragraph paragraph = classContent.paragraphs.get(i);
                if(paragraph instanceof Propty){
                    Propty propty = (Propty) paragraph;
                    checkPropertyApi(propty,i);
                }else if(paragraph instanceof Method){
                    Method method = (Method) paragraph;
                    checkMethodApi(method,i);
                }else{
                    ClassHeader classHeader = (ClassHeader) paragraph;
                    checkClassHeaderApi(classHeader, i);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        buttonOK.addActionListener(e -> onOK());

        buttonCancel.addActionListener(e -> onCancel());

        // call onCancel() when cross is clicked
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                onCancel();
            }
        });

        contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    private void checkPropertyApi(Propty propty,int i ) {
            Note note = propty.getNotes().stream().filter(nnote-> isProptyApiAnno(nnote)).findAny().orElse(null);
            boolean hasApi = note != null ;
            boolean hasAnno = propty.getAnnotation()!=null ;
            if(!hasApi && hasAnno){
                mount(propty,propty.getAnnotation(),i);
            }else if(!hasAnno){
                toWarning(propty,i);
            }
    }

    private void checkMethodApi(Method method,int i ) {
            Note note = method.getNotes().stream().filter(nnote-> isMethodApiAnno(nnote)).findAny().orElse(null);
            boolean hasApi = note != null ;
            boolean hasAnno = method.getAnnotation()!=null ;
            if(!hasApi && hasAnno){
                mount(method,method.getAnnotation(),i);
            }else if(!hasAnno){
                toWarning(method,i);
            }
    }

    private void checkClassHeaderApi(ClassHeader ch ,int i) {
        Note note = ch.getNotes().stream().filter(nnote-> isClassHeaderApiAnno(nnote)).findAny().orElse(null);
        boolean hasApi = note != null ;
        boolean hasAnno = ch.getAnnotation()!=null ;
        if(!hasApi && hasAnno){
            mount(ch,ch.getAnnotation(),i);
        }else if(!hasAnno){
            toWarning(ch,i);
        }
    }




    private void generatorRow(int i, JLabel label, JCheckBox jCheckBox, JLabel propyLabel,Paragraph propty) {
        panelRows.add(new PanelRow(i,label,jCheckBox,propyLabel,propty));
        listContent.add(jCheckBox,buildConstraint(i,0,1,1));
        listContent.add(propyLabel,buildConstraint(i,1,1,3));
        listContent.add(label,buildConstraint(i,4,1,0));
    }

    private void mount(Propty propty, Annotation annotation, int i) {
        JLabel propyLabel = new JLabel(propty.getProValue());
        JLabel label = new JLabel(propty.getAnnotation().getAnnotationInfo());
        JCheckBox jCheckBox = new JCheckBox("生成注解");
        jCheckBox.setSelected(true);
        generatorRow(i, label, jCheckBox, propyLabel,propty);
    }

    private void mount(Method method, Annotation annotation, int i) {
        JLabel methodLabel = new JLabel(method.getMethodHead());
        JLabel label = new JLabel(method.getAnnotation().getAnnotationInfo());
        JCheckBox jCheckBox = new JCheckBox("生成注解");
        jCheckBox.setSelected(true);
        generatorRow(i, label, jCheckBox, methodLabel,method);
    }

    private void mount(ClassHeader ch, Annotation annotation, int i) {
        JLabel classHeaderLabel = new JLabel("类");
        JLabel label = new JLabel(ch.getAnnotation().getAnnotationInfo());
        JCheckBox jCheckBox = new JCheckBox("生成注解");
        jCheckBox.setSelected(true);
        generatorRow(i, label, jCheckBox, classHeaderLabel,ch);
    }





    private void toWarning(Propty propty,int i ) {
        JLabel label = new JLabel("无注释");
        label.setForeground(Color.RED);
        JCheckBox jCheckBox = new JCheckBox("无法选取");
        jCheckBox.setEnabled(false);
        JLabel propyLabel = new JLabel(propty.getProValue());
        generatorRow(i, label, jCheckBox, propyLabel,propty);
    }

    private void toWarning(Method method,int i ) {
        JLabel label = new JLabel("无注释");
        label.setForeground(Color.RED);
        JCheckBox jCheckBox = new JCheckBox("无法选取");
        jCheckBox.setEnabled(false);
        JLabel methodLabel = new JLabel(method.getMethodHead());
        generatorRow(i, label, jCheckBox, methodLabel,method);
    }

    private void toWarning(ClassHeader classHeader,int i ) {
        JLabel label = new JLabel("无注释");
        label.setForeground(Color.RED);
        JCheckBox jCheckBox = new JCheckBox("无法选取");
        jCheckBox.setEnabled(false);
        JLabel chLabel = new JLabel("类");
        generatorRow(i, label, jCheckBox, chLabel,classHeader);
    }


    private GridBagConstraints buildConstraint(int row,int col,int gridheight,int gridwidth) {
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = col;
        c.gridy = row;
        c.gridheight = gridheight;
        c.gridwidth = gridwidth;
        c.weightx = 1.0;
        c.weighty = 1.0;
        c.fill = GridBagConstraints.BOTH;
        return c;
    }


    private boolean isProptyApiAnno(Note note) {
        if(note.getNoteName().equals(Cons.PROPTY_API)) { return true;}
        return false;
    }

    private boolean isMethodApiAnno(Note note) {
        if(note.getNoteName().equals(Cons.METHOD_API)) { return true;}
        return false;
    }

    private boolean isClassHeaderApiAnno(Note note) {
        if(note.getNoteName().equals(Cons.CLASS_API)) { return true;}
        return false;
    }




    private ClassContent classContent(String text) {
        Pattern pattern = Pattern.compile(Rex.CLASS_HEAD);
        Matcher matcher = pattern.matcher(text);
        if(matcher.find()){
            classHeader = new ClassHeader(0,matcher.start(),text.substring(0,matcher.start()),PType.CLASS_HEAD,matcher.start());
            return classContent(matcher);
        }
        return null;
    }

    private ClassContent classContent(Matcher matcher) {
        int start = matcher.start(6);
        int end = matcher.end(6);
        String paragraph = matcher.group(6);
        return new ClassContent(start,end,paragraph, PType.CLASS_CONTENT);
    }


    private void onOK() {
        insertNote();
        writeFile();
        dispose();
    }

    private void writeFile(){
        new WriteCommandAction.Simple(project, PsiManager.getInstance(project).findFile(virtualFile)) {
            @Override
            protected void run() throws Throwable {
                virtualFile.setBinaryContent(newStr.toString().getBytes("UTF-8"));
            }
        }.execute();
    }

    public void insertNote(){
        sortRows();
        for (int i = 0 ; i < panelRows.size(); i ++){
            PanelRow panelRow = panelRows.get(i);
            if(panelRow.getjCheckBox().isSelected()){
                if(subIndex == -1){
                    newStr = new StringBuffer(baseStr.substring(panelRow.getParagraph().getEnd(),baseStr.length()));
                    subIndex = panelRow.getParagraph().getEnd();
                }else{
                    if(panelRow.getParagraph().getEnd() != subIndex){
                        newStr.insert(0,baseStr.substring(panelRow.getParagraph().getEnd(),subIndex));
                        subIndex = panelRow.getParagraph().getEnd();
                    }
                }
                Paragraph paragraph = panelRow.getParagraph();
                String newParagraph;
                if(paragraph instanceof Propty){
                    newParagraph = generNewStr((Propty) paragraph);
                }else if(panelRow.getParagraph() instanceof Method){
                    newParagraph = generNewStr((Method) paragraph);
                }else{
                    newParagraph = generNewStr((ClassHeader) paragraph);
                }
                if(newParagraph != null) {newStr.insert(0,newParagraph);}
                subIndex = panelRow.getParagraph().getStart();
            }
        }
        if(subIndex != -1){
            newStr.insert(0,baseStr.substring(0,subIndex));
        }

    }

    private String generNewStr(Propty propty) {
        String anno = propty.getAnnotation().getAnnotationInfo();
        anno = anno.replaceAll("\\s+"," ");
        String insertInfo = Cons.PROPTY_INSERT_NOTE.replace(Cons.SIGN_ANNO,anno);
        return propty.getParagraphStr()
                .replace(propty.getProBody(),propty.getProIndent() + insertInfo + "\n" + propty.getProBody());
    }

    private String generNewStr(Method method) {
        String anno = method.getAnnotation().getAnnotationInfo();
        anno = anno.replaceAll("\\s+"," ");
        String insertInfo;
        if(method.getMethodType() != null) {
            insertInfo = Cons.METHOD_REQ_NOTE.replace(Cons.SIGN_ANNO,anno);
            insertInfo = insertInfo.replace(Cons.SIGN_METHOD_TYPE,method.getMethodType());
        }else{
            insertInfo = Cons.METHOD_NOTE.replace(Cons.SIGN_ANNO,anno);
        }
        return method.getParagraphStr().substring(0,method.getMethodStart())+ method.getMethodIndent()
                + insertInfo + method.getParagraphStr().substring(method.getMethodStart(),method.getParagraphStr().length());
    }

    @Nullable
    private String generNewStr(ClassHeader ch) {
        String anno = ch.getAnnotation().getAnnotationInfo();
        anno = anno.replaceAll("\\s+"," ");
        String insertInfo = Cons.CLASS_NOTE_.replace(Cons.SIGN_ANNO,anno);
        String path = getPath(ch);
        if(path != null) {
            path = path.replace("\"","");
            insertInfo = insertInfo.replace(Cons.SIGN_PATH,path);
            return ch.getParagraphStr().substring(0,ch.getClassHeaderStart()) + insertInfo + "\n" + ch.getParagraphStr().substring(ch.getClassHeaderStart(),ch.getParagraphStr().length());
        }
        return ch.getParagraphStr() ;
    }

    @Nullable
    private String getPath(ClassHeader ch) {
        for(Note note : ch.getNotes()){
            if(note.getNoteName().equals(Cons.ALL_NOTE)){
                for(NotePropety notePropety : note.getNotePropeties()){
                    if(notePropety.getKey().equals("name")){
                        return (String) notePropety.getValue();
                    }
                }
                return note.getDefaultPropeties();
            }
        }
        return null ;
    }


    private void sortRows() {
        Collections.sort(panelRows, (o1, o2) -> {
            if(o1.getI() > o2.getI()){
                return -1 ;
            }else if(o1.getI() < o2.getI()){
                return 1 ;
            }else{
                return 0;
            }
        });
    }

    private void onCancel() {
        // add your code here if necessary
        dispose();
    }

    public static void main(String[] args) {
//        ChooseDialog dialog = new ChooseDialog();
//        dialog.pack();
//        dialog.setVisible(true);
//        System.exit(0);
    }
}
